package uk.co.revisual.domino.proxies {
	
	import org.puremvc.as3.multicore.interfaces.IProxy;
	import org.puremvc.as3.multicore.patterns.proxy.Proxy;
	
	import uk.co.revisual.domino.consts.DominoConsts;
	import uk.co.revisual.domino.controller.cmaps.DominoCmdMap;
	import uk.co.revisual.domino.core.FSMModel;
	import uk.co.revisual.domino.errors.StateModelError;

	public class FSMModelProxy extends Proxy implements FSMModel
	{
	
		public function FSMModelProxy( data:Object = null ):void{
			super( DominoConsts.PROXY_NAMES.FSM_MODEL_PROXY, data );
		}
	
		public function getOutgoingActionsForState( statename:String ):Array
		{
			var actions:Array = [];
			var state:XML = getStateNode( statename );
			var transitions:XMLList = state.transition;
			
			for each(var t:XML in transitions )	{
				actions.push (t.@action.toString());		
			}
			return actions;
		}
		
		public function getConnectingAction( statename:String, targetstatename:String ):String
		{
			var transitions:XMLList = getTransitionsForState( statename );
			for each (var t:XML in transitions){
				var target:String = t.@target.toString()
				if( target == targetstatename ){
					return t.@action.toString();
				}
					
			}
			return null;
		}		
		
		public function getCommandPath( statename:String, notename:String ):String{

			var eventnodes:XMLList = getEventNodesForState( statename, notename );
			
			// check that there is an eventnode declared
			if( eventnodes.length() == 0 )return null;
						
			var xml:XML = eventnodes[0];
			var attribs:XMLList = eventnodes.attributes();
			var results:Array = [];
			
			// now we are going to loop through the attributes finding
			// those with a localname of "class" and if declared with 
			// namespace prepend the uri to is value
			for each( var a:XML in attribs ){
				var ns:Namespace = a.namespace();
				var localname:String = a.localName()
					
				// if the attribute has a namespace and localname is "class"
				// get the uri and add the value
				if( ns.prefix != "" && localname == DominoConsts.ATTRIBUTE_LOCAL_NAMES.CLASS){
					var path:String = ns.uri;
					var name:String = a.toString();
					results.push( path + "." + name );
				} 
				// if no namespace is used, just return the value
				else if (localname == DominoConsts.ATTRIBUTE_LOCAL_NAMES.CLASS){
					results.push( a.toString() );
				}
			}
			
			if( results.length == 0 )throw new StateModelError( StateModelError.NO_CLASS_ATTRIBUTE, statename, notename );
			else if( results.length > 1 )throw new StateModelError( StateModelError.MULTIPLE_CLASS_ATTRIBUTES, statename, notename );
			return String( results[0] );
		}
		
		override public function setData(data:Object):void{
			super.setData( parseRawData( XML( data ) ) );
		}
						
		private function parseRawData( data:XML ) : XML {
			var list:XMLList = data.state;
			// loops through each state node, applys entering, exiting and changed 
			// attributes if required
			for each( var xml:XML in list ) {
				
				// check the number of each eventnodes
				// if > 1 throw error
				var entered:int = xml.elements( DominoConsts.EVENT_NODES_NAMES.ENTERED ).length();
				var teardown:int = xml.elements(  DominoConsts.EVENT_NODES_NAMES.TEAR_DOWN ).length();
				var enteringGuard:int = xml.elements(DominoConsts.EVENT_NODES_NAMES.ENTERING_GUARD ).length();
				var exitingGuard:int = xml.elements( DominoConsts.EVENT_NODES_NAMES.EXITING_GUARD ).length();
				var cancelled:int = xml.elements( DominoConsts.EVENT_NODES_NAMES.CANCELLED ).length();
				
				if( entered > 1 ) throw new StateModelError( StateModelError.MULTIPLE_NODES, xml.@name, DominoConsts.EVENT_NODES_NAMES.ENTERED );
				if(	teardown > 1 ) throw new StateModelError( StateModelError.MULTIPLE_NODES, xml.@name, DominoConsts.EVENT_NODES_NAMES.TEAR_DOWN );
				if(	enteringGuard > 1 ) throw new StateModelError( StateModelError.MULTIPLE_NODES, xml.@name, DominoConsts.EVENT_NODES_NAMES.ENTERING_GUARD );
				if( exitingGuard > 1 ) throw new StateModelError( StateModelError.MULTIPLE_NODES, xml.@name, DominoConsts.EVENT_NODES_NAMES.EXITING_GUARD );
				if( cancelled > 1 )  throw new StateModelError( StateModelError.MULTIPLE_NODES,  xml.@name, DominoConsts.EVENT_NODES_NAMES.CANCELLED );
				
				// add attributes to state where needed
				xml.@changed = DominoConsts.INTERNAL.CHANGED;
				if( Boolean( enteringGuard ) ) xml.@entering = DominoConsts.EVENT_NODES_NAMES.ENTERING_GUARD;
				if( Boolean( exitingGuard) ) xml.@exiting = DominoConsts.EVENT_NODES_NAMES.EXITING_GUARD;
				
				
			}
			return data;
		}
		
		private function getStateNode( statename:String ):XML{
			if( statename == null || statename == "" ) throw new StateModelError( StateModelError.NULL_STATENAME_PASSED );
			var states:XMLList = fsm.state.(@name == statename );
			if( states.length() == 0 )
				throw new StateModelError( StateModelError.STATE_NOT_FOUND, statename );
			else if( states.length() > 1)
				throw new StateModelError( StateModelError.MULTIPLE_STATES, statename );
			return states[0];
		}
		
		private function getTransitionsForState( statename:String ):XMLList{
			var state:XML = getStateNode( statename );
			return 	state.transition;		
		}
		
		private function getEventNodesForState( statename:String, notename:String ):XMLList{
			if (notename == null || notename == "" ) throw new StateModelError(	StateModelError.NULL_NOTENAME_PASSED );  
			var state:XML = getStateNode( statename );
			return state.elements( notename );
			
		}

		private function get fsm():XML{
			var xml:XML  = data as XML;
			if( xml == null ) throw new StateModelError( StateModelError.NULL_DATA );
			return XML( data );
		}
		
		

	}
}

